package com.ybg.bookrules.services.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ybg.bookrules.commons.BoomValication;
import com.ybg.bookrules.mapper.UserinfosMapper;
import com.ybg.bookrules.module.dto.MenusDTO;
import com.ybg.bookrules.module.dto.UserinfosDTO;
import com.ybg.bookrules.module.entity.Menus;
import com.ybg.bookrules.module.entity.Userinfos;
import com.ybg.bookrules.module.vo.UserinfosVO;
import com.ybg.bookrules.services.MenusService;
import com.ybg.bookrules.services.UserinfosService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserinfosServiceImpl extends ServiceImpl<UserinfosMapper, Userinfos>
        implements UserinfosService {
    @Resource
    private MenusService menusService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Value("${redis.rule}")
    private String bigkey;
    @Resource
    private BoomValication boomValication;
    @Resource
    private BoomValication nameBoom;

    @PostConstruct
    public void initBoom() {
        //将所有的用户和密码存放到boom过滤器中  将用户名存放到boom过滤器中
        this.getBaseMapper().selectList(null)
                .stream().forEach(u -> {
                    boomValication.put(u.getUsername() + "_" + u.getPassword());
                    nameBoom.put(u.getUsername());
                });
    }

    /**
     * 登录服务
     *
     * @param username
     * @param password
     * @return
     */
    @Override
    public UserinfosDTO userLogin(String username, String password) {
        //1.接收用户的用户名和密码  先对密码进行MD5加密
        String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
        //1.1优先搜索redis数据库，如果存在则直接返回
        String smallkey = username + "_" + md5Password;
        Object res = stringRedisTemplate.opsForHash().get(bigkey, smallkey);
        //1.2生成临时token
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        System.out.println(token);
        if (res == null) {
            //使用boom过滤器检查是否有用户输入的对象 如果没有则直接返回null 如果有则去查数据库
            if (boomValication.exists(smallkey)) {
                //2.使用QueryWrapper对用户名和密码查询进行条件封装
                QueryWrapper<Userinfos> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("username", username).eq("password", md5Password);
                //3.查询的用户对象中获取的用户userid
                Userinfos user = this.getBaseMapper().selectOne(queryWrapper);
                if (user != null) {
                    //4.使用用户id到MenusMapper中？MenusService中查询对应的菜单
                    List<Menus> menus = menusService.selectMenusAndUseridByUserid(user.getUserid());

                    //5.将菜单和用户信息存放到UserinfosDTO中并返回
                    //5.1 将List<Menus>转换成List<MenusDTO>
                    List<MenusDTO> mdto = menus.stream().map(menu -> MenusDTO.builder()
                            .menuid(menu.getMenuid())
                            .menuname(menu.getMenuname())
                            .build()).collect(Collectors.toList());
                    //5.2 将userinfos对象中的属性复制到UserinfosDTO对象下  同时生成一个token
                    UserinfosDTO usdto = UserinfosDTO.builder().menus(mdto)
                            .token(UUID.randomUUID().toString().replaceAll("-", ""))
                            .build();
                    BeanUtils.copyProperties(user, usdto);
                    //5.3将用户名和token 存放在缓存数据库中 并设置失效时间
                    stringRedisTemplate.opsForValue().set(token, usdto.toString(), 1, TimeUnit.MINUTES);
                    return usdto;
                } else {
                    //未找到对应用户返回null
                    System.out.println("mysql未找到用户");
                    return null;
                }
            } else {
                System.out.println("boom过滤器未找到用户");
                return null;
            }
        } else {
            //使用缓存服务器上的信息进行登录
            String[] infos = res.toString().split(",");
            UserinfosDTO udto = UserinfosDTO.builder()
                    .userid(Integer.parseInt(infos[0]))
                    .username(infos[2])
                    .realname(infos[3])
                    .gender(Integer.parseInt(infos[4]))
                    .carid(infos[5])
                    .address(infos[6])
                    .telephone(infos[7])
                    .token(token)
                    .build();
            //5.3将用户名和token 存放在缓存数据库中 并设置失效时间
            stringRedisTemplate.opsForValue().set(token, udto.toString(), 1, TimeUnit.MINUTES);
            return udto;
        }
    }

    /**
     * 用户名验证，验证用户是否存在；
     * 用户不存在则可以注册；
     */
    public boolean existsUserByUsername(String username) {
        return nameBoom.exists(username);
    }

    /**
     * 注册服务
     */
    @Transactional
    public boolean userRegister(UserinfosVO uvo) {
        try {
            //将UserinfosVO对象转换成Userinfos对象
            Userinfos us = new Userinfos();
            BeanUtils.copyProperties(uvo, us);
            us.setRoleid(2);//只能注册普通会员用户
            us.setPassword(DigestUtils
                    .md5DigestAsHex(us.getPassword()
                            .getBytes()));//对密码进行MD5加密
            us.setRegistertime(new Date());
            us.setDeleted(0);
            //将对象保存到mysql数据库
            boolean saveResult = this.save(us);//this.getBaseMapper().insert(us);也可以
            if (!saveResult) {
                throw new RuntimeException("用户信息保存失败");
            }
            //将该用户存放到缓存数据库中
            stringRedisTemplate.opsForHash().put(bigkey, us.getUsername() + "_" + us.getPassword(), us.toString());
            //将用户名存放到boom过滤器中
            boomValication.put(us.getUsername() + "_" + us.getPassword());
            nameBoom.put(us.getUsername());
            return true;
        } catch (Exception e) {
            // 记录异常日志
            e.printStackTrace();
            return false;
        }
    }
}

